import datetime
import random
import re

from flask import request, abort, current_app, make_response, jsonify, session, g
from info import redis_store, constants, db
from info.libs.yuntongxun.sms import CCP
from info.models import User
from info.modules.api_v1 import api_blu
from info.response_code import RET
from info.utils.captcha.captcha import captcha
from info.utils.common import user_login


@api_blu.route('/imagecode')
def img_code():

    image_code_id = request.args.get('cur')
    pre_img_code_id = request.args.get('pre')  # 非必选

    if not image_code_id:
        abort(404)

    _, text, image = captcha.generate_captcha()

    current_app.logger.info('图片验证码为 %s' % text)

    try:
        redis_store.setex('ImageCodeId_' + image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
    except Exception as e:
        # 若存储失败, 在控制台输出错误日志, 并抛出异常
        current_app.logger.error(e)
        abort(500)

    # 返回 图片验证码 响应
    response = make_response(image)
    # 设置响应头 图片类型 为 image/jpg
    response.headers['Content-Type'] = 'image/jpg'

    return response


@api_blu.route('/smscode', methods=['POST'])
def sms_code():
    # 1> 接收参数: 手机号, 图片验证码, UUID
    mobile = request.json.get('mobile')
    image_code = request.json.get('image_code')
    image_code_id = request.json.get('image_code_id')

    # 2> 校验数据的完整性
    if not all([mobile, image_code, image_code_id]):
        # 返回 json 类型的 响应数据, response_code.py 模块中
        # 条件出错时, 有响应的错误信息提示: errno 指错误码, errmsg 指错误信息
        return jsonify(errno=RET.PARAMERR, errmsg='参数不全')

    # 3> 校验手机号, 正则r"1[35678]\d{9}"
    if not re.match(r"1[35678]\d{9}", mobile):
        # 返回 json 类型的 响应数据
        return jsonify(errno=RET.PARAMERR, errmsg='手机号格式不正确')

    # 4> 从redis数据库中 取出 图片验证码的 文本类型数据
    try:
        redis_image_code = redis_store.get('ImageCodeId_' + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库连接失败')

    # 5> 是否查询到验证码, 若不存在, 则表示验证码过期
    if not redis_image_code:
        return jsonify(errno=RET.NODATA, errmsg='图片验证码过期')

    # 6> 校验 图片验证码的一致性
    # if image_code != redis_image_code:
    # 区分图片验证码的大小写
    if image_code.upper() != redis_image_code.upper():
        return jsonify(errno=RET.DBERR, errmsg='图片验证码输入错误')

    # 7> 自定义 6位数 短信验证码
    sms_code_str = '%06d' % random.randint(0, 999999)  # 格式化, 用 0 补全,保证六位数
    # 在控制台输出 短信验证码
    current_app.logger.info('短信验证码为 %s' % sms_code_str)

    # 8> 将自定义的短信验证码和手机号 发送给第三方平台
    # result = CCP().send_template_sms(mobile, [sms_code_str, constants.SMS_CODE_REDIS_EXPIRES], 1)
    # if result != 0:
    #     return jsonify(errno=RET.THIRDERR, errmsg='网络连接错误,短信验证码发送失败')

    # 9> 将短信验证码 以KV的形式(手机号为ket, 验证码为value) 保存到 redis 数据中
    try:
        redis_store.setex('SMS_' + mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code_str)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='短信验证码保存失败')

    # 10> 返回响应数据
    return jsonify(errno=RET.OK, errmsg='短信验证码发送成功')


@api_blu.route('/user', methods=['POST'])
def register():
    # 1> 接收字典中的参数: 手机号, 短信验证码, 密码
    mobile = request.json.get('mobile')
    phonecode = request.json.get('phonecode')
    password = request.json.get('password')

    # 2> 校验数据的完整性
    if not all([mobile, phonecode, password]):
        # 返回 json 类型的 响应数据, response_code.py 模块
        return jsonify(errno=RET.PARAMERR, errmsg='参数不全')

    # 3> 校验手机号, 正则r"1[35678]\d{9}"
    if not re.match(r"1[35678]\d{9}", mobile):
        # 返回 json 类型的 响应数据
        return jsonify(errno=RET.PARAMERR, errmsg='手机号格式不正确')

    # 4> 从redis数据库中 取出 短信验证码
    # 注意: 保存格式 SMS_mobile, 不要忘记前缀
    try:
        redis_smscode = redis_store.get('SMS_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库连接失败')

    # 5> 是否查询到验证码, 若不存在, 则表示验证码过期
    if not redis_smscode:
        return jsonify(errno=RET.NODATA, errmsg='短信验证码过期')

    # 6> 校验 图片验证码的一致性
    if phonecode != redis_smscode:
        return jsonify(errno=RET.DBERR, errmsg='短信验证码输入错误')

    # 7> 实例化User模型类, 往sql数据表中添加用户数据
    user = User()
    # 用户账号为手机号
    user.name = mobile
    # 用户手机号码
    user.mobile = mobile
    #  通过更改 models.py 中的密码 进行加密
    user.password = password

    # 提交并添加用户数据
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='用户数据保存失败')

    # TODO session 保持用户登录状态, 往 session 中保存数据表示当前已经登录
    session['user_id'] = user.id

    # 10> 注册成功, 返回响应数据
    return jsonify(errno=RET.OK, errmsg='注册成功')


@api_blu.route('/session', methods=['POST'])
def login():
    # 1> 接收字典中的参数: 手机号, 密码
    mobile = request.json.get('mobile')
    password = request.json.get('password')

    # 2> 校验数据的完整性
    if not all([mobile, password]):
        # 返回 json 类型的 响应数据, 错误时有相应提示
        return jsonify(errno=RET.PARAMERR, errmsg='参数不全')

    # 3> 校验手机号, 正则r"1[35678]\d{9}"
    if not re.match(r"1[35678]\d{9}", mobile):
        # 返回 json 类型的 响应数据
        return jsonify(errno=RET.PARAMERR, errmsg='手机号格式不正确')

    # 4> 从 sql 数据库中获取 手机号
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库连接失败')

    # 5> 校验 sql数据库中手机号是否存在
    if not user:
        return jsonify(errno=RET.NODATA, errmsg='用户不存在')

    # 6> 校验输入的密码 与 数据库中加密的密码是否一致
    if not user.check_passowrd(password):
        return jsonify(errno=RET.NODATA, errmsg='密码输入错误')

    # 7> 更新用户登录时间
    user.update_time = datetime.datetime.now()
    try:
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='用户数据保存失败')

    # TODO session 保持用户登录状态, 往 session 中保存数据表示当前已经登录
    session['user_id'] = user.id

    # 10> 登录成功, 返回响应数据
    return jsonify(errno=RET.OK, errmsg='登录成功')


@api_blu.route('/session')
@user_login
def login_status():
    """ 状态保持 """
    # 获取用户
    user = g.user

    if not user:
        return jsonify(errno=RET.SESSIONERR, errmsg='用户未登录')

    data = {
        'name': user.name,
        'user_id': user.id
    }

    return jsonify(errno=RET.OK, errmsg='登录成功', data=data)


@api_blu.route('/session', methods=['DELETE'])
def logout():
    """
    退出函数
    """
    # 删除 session
    session.pop('user_id', None)
    # 返回响应
    return jsonify(errno=RET.OK, errmsg='退出成功')


